package worker

import (
	"context"
	"go.etcd.io/etcd/clientv3"
	"go_job/common"
)

//etcd txn事务
type JobLock struct {
	kv      clientv3.KV
	lease   clientv3.Lease
	jobName string
	cancel  context.CancelFunc //终止续租
	leaseId clientv3.LeaseID
	isLock  bool
}

func InitJobLock(kv clientv3.KV, lease clientv3.Lease, jobName string) (jobLock *JobLock) {
	jobLock = &JobLock{
		kv:      kv,
		lease:   lease,
		jobName: jobName,
	}
	return
}
func (jobLock *JobLock) TryLock() (err error) {
	var (
		leaseResp    *clientv3.LeaseGrantResponse
		cancelCtx    context.Context
		cancelFunc   context.CancelFunc
		leaseId      clientv3.LeaseID
		keepRespChan <-chan *clientv3.LeaseKeepAliveResponse //只读channel
		txn          clientv3.Txn
		lockKey      string
		txnResp      *clientv3.TxnResponse
	)
	//创建租约 5s
	if leaseResp, err = jobLock.lease.Grant(context.TODO(), 5); err != nil {
		return
	}
	cancelCtx, cancelFunc = context.WithCancel(context.TODO())
	leaseId = leaseResp.ID
	//自动续租
	if keepRespChan, err = jobLock.lease.KeepAlive(cancelCtx, leaseId); err != nil {
		goto FAIL
	}
	//处理续租应答的协程
	go func() {
		var (
			resp *clientv3.LeaseKeepAliveResponse
		)
		for {
			select {
			case resp = <-keepRespChan:
				if resp == nil {
					goto END
				}
			}
		}
	END:
	}()
	//创建txn事务
	txn = jobLock.kv.Txn(context.TODO())
	lockKey = common.JOB_LOCK_DIR + jobLock.jobName
	//事务抢锁
	txn.If(clientv3.Compare(clientv3.CreateRevision(lockKey), "=", 0)).
		Then(clientv3.OpPut(lockKey, "", clientv3.WithLease(leaseId))).
		Else(clientv3.OpGet(lockKey))
	if txnResp, err = txn.Commit(); err != nil {
		goto FAIL
	}
	//成功返回,失败释放

	if !txnResp.Succeeded {
		err = common.ERR_LOCAL_ALREADY_IN_USE
		goto FAIL
	}
	jobLock.leaseId = leaseId
	jobLock.cancel = cancelFunc
	jobLock.isLock = true
	return
FAIL:
	cancelFunc()
	jobLock.lease.Revoke(context.TODO(), leaseId)
	return
}

func (jobLock *JobLock) Unlock() {
	if jobLock.isLock {
		jobLock.cancel()
		jobLock.lease.Revoke(context.TODO(), jobLock.leaseId)
	}

}
